package cn.b.sky.shop.activiti.cmd;

import cn.b.sky.shop.activiti.constans.GlobalDefine;
import cn.b.sky.shop.activiti.dao.FlowNodeDao;
import cn.b.sky.shop.activiti.dao.FlowNodeEventDao;
import cn.b.sky.shop.activiti.dao.FlowNodeUserDao;
import cn.b.sky.shop.activiti.enums.FlowNodeUserEnum;
import cn.b.sky.shop.activiti.model.FlowNode;
import cn.b.sky.shop.activiti.model.FlowNodeEvent;
import cn.b.sky.shop.activiti.model.FlowNodeUser;
import cn.b.sky.common.utils.SpringBeanUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.bpmn.model.*;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.impl.cmd.GetModelEditorSourceCmd;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 把xml解析的内存模型保存到数据库里.
 */
public class SyncProcessModelCmd implements Command<Void> {
  // 日志对象
  private static final Logger LOGGER= LoggerFactory.getLogger(SyncProcessModelCmd.class);

  /** 流程模型id. */
  private final String modelId;

  private final String modelKey;

  private final String tenantId;

  /**
   * 构造方法.
   */
  public SyncProcessModelCmd(String modelId, String modelKey, String tenantId) {
    this.modelId = modelId;
    this.modelKey = modelKey;
    this.tenantId = tenantId;
  }

  @Override
  public Void execute(CommandContext commandContext) {
    byte[] bytes = new GetModelEditorSourceCmd(modelId).execute(commandContext);
    if (bytes == null) {
      return null;
    }
    JsonNode modelNode = null;
    try {
      modelNode = new ObjectMapper().readTree(bytes);
    } catch (Exception e) {
      LOGGER.error("{}", e);
    }
    BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
    Map<String, GraphicInfo> graphicInfoMap = model.getLocationMap();
    for (Map.Entry<String, GraphicInfo> entry : graphicInfoMap.entrySet()) {
      String key = entry.getKey();
      FlowElement flowElement = model.getFlowElement(key);
      if (flowElement instanceof UserTask) {
        this.processUserTask(flowElement);
      } else if (flowElement instanceof StartEvent) {
        this.processStartEvent(flowElement);
      } else if (flowElement instanceof EndEvent) {
        this.processEndEvent(flowElement);
      } else {
        continue;
      }
    }
    return null;
  }

  private void processEndEvent(FlowElement flowElement) {
    if (!StringUtils.isBlank(modelId)) {
      FlowNodeDao flowNodeDao = getFlowNodeDao();
      FlowNode node = flowNodeDao.selectFlowNodeByModelIdAndCode(modelId, tenantId, flowElement.getId());
      if (node == null) {
        node = new FlowNode();
      }
      node.setModelId(modelId);
      node.setType(GlobalDefine.WorkFlow.ENDEVENT);
      node.setName(flowElement.getName());
      node.setModelKey(modelKey);
      node.setTenantId(tenantId);
      node.setCode(flowElement.getId());
      if (node.getId() == null) {
        flowNodeDao.saveFlowNode(node);
      } else {
        flowNodeDao.updateFlowNode(node);
      }
      this.processListener(flowElement.getExecutionListeners(), node);
    }

  }

  private void processStartEvent(FlowElement flowElement) {
    if (!StringUtils.isBlank(modelId)) {
      FlowNodeDao flowNodeDao = getFlowNodeDao();
      FlowNode node = flowNodeDao.selectFlowNodeByModelIdAndCode(modelId, tenantId, flowElement.getId());
      if (node == null) {
        node = new FlowNode();
      }
      StartEvent event = (StartEvent)flowElement;
      node.setModelId(modelId);
      node.setType(GlobalDefine.WorkFlow.STARTEVENT);
      node.setName(flowElement.getName());
      node.setModelKey(modelKey);
      node.setCode(flowElement.getId());
      //开始表单的formkey
      node.setFormKey(event.getFormKey());
      node.setTenantId(tenantId);
      if (node.getId() == null) {
        flowNodeDao.saveFlowNode(node);
      } else {
        flowNodeDao.updateFlowNode(node);
      }
      this.processListener(flowElement.getExecutionListeners(), node);
    }

  }

  private void processUserTask(FlowElement flowElement) {
    if (!StringUtils.isBlank(modelId)) {
      FlowNodeDao flowNodeDao = getFlowNodeDao();
      UserTask userTask = (UserTask) flowElement;

      FlowNode node = flowNodeDao.selectFlowNodeByModelIdAndCode(modelId, tenantId, userTask.getId());
      if (node == null) {
        node = new FlowNode();
      }
      node.setModelId(modelId);
      node.setType(GlobalDefine.WorkFlow.USAERTASK);
      node.setFormKey(userTask.getFormKey());
      node.setName(userTask.getName());
      node.setCode(userTask.getId());
      node.setModelKey(modelKey);
      node.setTenantId(tenantId);
      if (node.getId() == null) {
        flowNodeDao.saveFlowNode(node);
      } else {
        flowNodeDao.updateFlowNode(node);
      }
      // 配置监听器
      this.processListener(userTask.getExecutionListeners(), node);
      this.processListener(userTask.getTaskListeners(), node);
      //配置参与者
      int index = 1;
      index = this.processUserTaskConf(node, userTask.getAssignee(), FlowNodeUserEnum.ASSIGNEE.getCode(), index);

      for (String candidateUser : userTask.getCandidateUsers()) {
        index = this.processUserTaskConf(node, candidateUser, FlowNodeUserEnum.CANDIDATEUSERS.getCode(), index);
      }

      for (String candidateGroup : userTask.getCandidateGroups()) {
        this.processUserTaskConf(node, candidateGroup, FlowNodeUserEnum.CANDIDATEGROUPS.getCode(), index);
      }
    }
  }

  private int processUserTaskConf(FlowNode node, String assignee, String type, int index) {

    if (assignee == null) {
      return index;
    }
    FlowNodeUserDao flowNodeUserDao = getFlowNodeUserDao();
    FlowNodeUser flowNodeUser = new FlowNodeUser();
    flowNodeUser.setFlowNodeId(node.getId());
    flowNodeUser.setValue(assignee);
    flowNodeUser.setStatus(0);
    flowNodeUser.setType(type);
    flowNodeUser.setPriority(index);
    FlowNodeUser selectflowNodeUser = flowNodeUserDao.selectFlowNodeUserByCondition(flowNodeUser);
    if (selectflowNodeUser == null) {
      flowNodeUserDao.insertFlowNodeUser(flowNodeUser);
    }
    return index++;
  }

  private void processListener(List<ActivitiListener> activitiListeners, FlowNode node) {
    Map<String, Integer> eventTypeMap = new HashMap<String, Integer>();
    eventTypeMap.put("start", 0);
    eventTypeMap.put("end", 1);
    eventTypeMap.put("take", 2);
    eventTypeMap.put("create", 3);
    eventTypeMap.put("assignment", 4);
    eventTypeMap.put("complete", 5);
    eventTypeMap.put("delete", 6);
    FlowNodeEventDao flowNodeEventDao = getFlowNodeEventDao();
    for (ActivitiListener activitiListener : activitiListeners) {
      String value = activitiListener.getImplementation();
      int type = eventTypeMap.get(activitiListener.getEvent());
      FlowNodeEvent event = new FlowNodeEvent();
      event.setFlowNodeId(node.getId());
      event.setStatus(0);
      event.setType(type);
      event.setValue(value);
      List<FlowNodeEvent> selectEvent = flowNodeEventDao.selectFlowNodeEventByCondition(event);
      if (selectEvent == null || selectEvent.size() <= 0) {
        flowNodeEventDao.insertFlowNodeEvent(event);
      }
    }
  }

  public FlowNodeDao getFlowNodeDao() {
    return SpringBeanUtil.getBean(FlowNodeDao.class);
  }

  public FlowNodeEventDao getFlowNodeEventDao() {
    return SpringBeanUtil.getBean(FlowNodeEventDao.class);
  }

  public FlowNodeUserDao getFlowNodeUserDao() {
    return SpringBeanUtil.getBean(FlowNodeUserDao.class);
  }
}
